﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using Internal.UIOC;
using UIOC.Attributes;

namespace UIOC {

    /// <summary>
    /// 框架环境
    /// </summary>
    public interface IRuntime {

        CancellationToken QuitCancellationToken { get; }

        bool IsQuit { get; }

        bool IsPlaying { get; }
    }

    internal sealed class Runtime : IRuntime, IRuntimeInternal {

        private readonly CancellationTokenSource m_QuitCancellationTokenSource = new();
        public CancellationToken QuitCancellationToken => m_QuitCancellationTokenSource.Token;

        public bool IsQuit { get; private set; }

        public bool IsPlaying {
            get {
                if (UnityEngine.Application.isPlaying && !IsQuit) {
                    return true;
                }
                throw new ApplicationException("应用程序退出");
            }
        }

        [DebuggerHidden]
        public bool IsCancellationRequested(CancellationToken cancellationToken) {
            return QuitCancellationToken.IsCancellationRequested || cancellationToken.IsCancellationRequested;
        }


        [Conditional("UNITY_EDITOR")]
        [Conditional("DEBUG")]
        [DebuggerHidden]
        public void AssertPlaying() {
#if !UNITY_INCLUDE_TESTS
            if (!IsPlaying) {
            }
#elif UNITY_EDITOR
            if (UnityEditor.EditorApplication.isPlaying || UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) {
                if (!IsPlaying) {
                }
            }
#endif
        }

        internal void Quit() {
            IsQuit = true;
            m_QuitCancellationTokenSource.Cancel();
        }

        void IRuntimeInternal.RegisterAssembly(Assembly assembly, bool logEnabled) {
            var registeredSet = new HashSet<Type>();
            foreach (var type in assembly.DefinedTypes) {
                RegisterType(type, registeredSet, Application.GetTypeMap(), logEnabled);
            }
        }

        private static void RegisterType(Type type, HashSet<Type> registeredSet, ITypeMapInternal typeMap, bool logEnabled = true) {
            if (type.IsInterface || type.IsAbstract || type.IsEnum) {
                return;
            }
            var beanRegisterAttribute = type.GetCustomAttribute<BeanRegisterAttribute>();
            if (beanRegisterAttribute != null) {
                var objectType = beanRegisterAttribute.ObjectType;
                var implementAttributes = objectType.BaseType.GetCustomAttributes<ImplementAttribute>();
                var interfaces = objectType.GetInterfaces();
                var instanceTypeAttribute = objectType.GetInterfaces().Select(@interface => @interface.GetCustomAttribute<InstanceTypeAttribute>(true)).Where(item => item != null).FirstOrDefault();
                foreach (var attribute in implementAttributes) {
                    if (attribute?.InterfaceType != null) {
                        var interfaceType = attribute.InterfaceType;
                        UnityEngine.Assertions.Assert.IsFalse(
                            registeredSet.Contains(interfaceType), 
                            $"单个 [程序集({type.AssemblyQualifiedName})] 中发现多个 [目标类型({nameof(ImplementAttribute)}.{nameof(ImplementAttribute.InterfaceType)}={interfaceType.FullName})] 映射 \n当前 [类型({type.FullName})], 已经存在的 [类型({(registeredSet.TryGetValue(interfaceType, out var actualValue) ? actualValue.FullName : null)})]"
                        );
                        typeMap.Register(interfaceType, type, instanceTypeAttribute?.Value ?? InstanceType.Default, logEnabled);
                        registeredSet.Add(interfaceType);
                    }
                }
            }
        }
    }
}

namespace Internal.UIOC {

    public interface IRuntimeInternal {

        void RegisterAssembly(Assembly assembly, bool logEnabled = true);
    }
}
